package com.hup.utils.commons;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

/**
 * @author hugan
 * @date 2019/8/4
 */
@SuppressWarnings("SpellCheckingInspection")
public class DateBuilder {

    private static final ThreadLocal<SimpleDateFormat> DF = ThreadLocal.withInitial(SimpleDateFormat::new);

    private static final String[] WEEKDAY_CHN = {"一", "二", "三", "四", "五", "六", "日"};
    public static final String YMD = "yyyy-MM-dd";
    public static final String YMD_HMS = "yyyy-MM-dd HH:mm:ss";
    public static final String YMD_HMS_S = "yyyy-MM-dd HH:mm:ss:SSS";
    public static final String HMS = "HH:mm:ss";
    public static final String YYMMDD_HHMMSS = "yyMMdd_HHmmss";


    //时间运算 链式调用方法
    private final Calendar c;

    private DateBuilder(Date time) {
        c = Calendar.getInstance();
        c.setTime(time);
    }

    //静态链式调用 起始方法
    public static DateBuilder use(Date time) {
        return new DateBuilder(time);
    }

    public static DateBuilder use() {
        return new DateBuilder(new Date());
    }

    //链式调用 获取结果
    public Date time() {
        return c.getTime();
    }

    public Date cleanTime() {
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.clear(Calendar.MINUTE);
        c.clear(Calendar.SECOND);
        c.clear(Calendar.MILLISECOND);
        return c.getTime();
    }

    //时间运算 链式方法 扩展方法.st
    public DateBuilder nextDay() {
        c.add(Calendar.DAY_OF_YEAR, 1);
        return this;
    }

    /**
     * 偏移day天,可以是正负数
     */
    public DateBuilder addDay(int day) {
        c.add(Calendar.DAY_OF_YEAR, day);
        return this;
    }

    public DateBuilder addHour(int val) {
        c.add(Calendar.HOUR_OF_DAY, val);
        return this;
    }

    public DateBuilder nextMonth() {
        c.add(Calendar.MONTH, 1);
        return this;
    }

    public DateBuilder lastMonthFirstDay() {
        c.add(Calendar.MONTH, -1);
        c.set(Calendar.DAY_OF_MONTH, 1);
        return this;
    }

    public DateBuilder curMonthLastDay() {
        int maxDay = c.getActualMaximum(Calendar.DAY_OF_MONTH);
        c.set(Calendar.DAY_OF_MONTH, maxDay);
        return this;
    }
    //时间运算 链式方法 扩展方法.ed

    //格式化或parse相关方法

    /**
     * 构造严格模式的df
     */
    public static SimpleDateFormat strictFormat(String format) {
        SimpleDateFormat res = new SimpleDateFormat(format);
        res.setLenient(false);
        return res;
    }

    public static String ymd(Date date) {
        return format(date, YMD);
    }

    public static String ymdHms(Date date) {
        return format(date, YMD_HMS);
    }

    public static String hms(Date date) {
        return format(date, HMS);
    }

    public static String yyMMdd_HHmmss(Date date) {
        return format(date, YYMMDD_HHMMSS);
    }

    /**
     * @param str 不能为null或空字符串!
     */
    public static Date ymd(String str) {
        return parse(str, YMD);
    }

    public static String format(Date date, String format) {
        if (date == null) {
            return "";
        }
        SimpleDateFormat df = DF.get();
        df.applyPattern(format);
        return df.format(date);
    }

    /**
     * 校验日期入参是否正确,如防止sql注入/输入不严谨
     *
     * @param dateStr notEmptyString
     */
    public static Date validDate(String dateStr, String format) {
        Date parse = parse(dateStr, format);
        /*
              format='yyyy-MM-dd',dateStr='2019-12-15' or '1'='1'
              2020-01-1abc
            是不会有ParseException的
            所以需要将parse重新格式化成字符串,和dateStr比较
         */
        if (!dateStr.equals(format(parse, format))) {
            throw new RuntimeException("[" + dateStr + "]转换错误");
        }
        return parse;
    }

    public static Date parse(String str, String format) {
        try {
            if (str == null) {
                throw new RuntimeException("[null]转换错误");
            }
            SimpleDateFormat df = DF.get();
            df.applyPattern(format);
            df.setLenient(false);//开启严格模式
            return df.parse(str);
        } catch (ParseException e) {
            throw new RuntimeException("[" + str + "]转换错误");
        }
    }

    public static Date parseNull(String str, String format) {
        try {
            SimpleDateFormat df = DF.get();
            df.applyPattern(format);
            df.setLenient(false);//开启严格模式
            return df.parse(str);
        } catch (ParseException e) {
            return null;
        }
    }

    //格式化时间段
    public static String spanChar(long ms) {
        return spanChar(ms, false);
    }

    /**
     * 用中文的时长
     *
     * @param ms 毫秒
     * @return x小时x分x秒x毫秒
     */
    public static String spanCharMs(long ms) {
        return spanChar(ms, true);
    }

    /**
     * 用中文的时长
     *
     * @param ms 毫秒
     * @return x小时x分x秒x毫秒
     */
    public static String spanChar(long ms, boolean withMs) {
        if (ms == 0) {
            return withMs ? "0毫秒" : "0秒";
        }
        StringBuilder sb = new StringBuilder();
        if (ms < 0) {
            sb.append('-');
            ms = -ms;
        }
        int s = (int) (ms / 1000);
        if (s >= 86400) {
            int day = s / 86400;
            sb.append(day).append("天");
        }
        if (s >= 3600) {
            int h = s % 86400 / 3600;
            sb.append(h).append("小时");
        }
        if (s >= 60) {
            int m = s % 3600 / 60;
            sb.append(m).append("分");
        }
        s = s % 60;
        if (s != 0) {
            sb.append(s).append("秒");
        }
        if (withMs || sb.length() == 0) {
            ms = ms % 1000;
            if (ms != 0) {
                sb.append(ms).append("豪秒");
            }
        }
        return sb.toString();
    }

    /**
     * 时区转换
     */
    public static Date GMT2MGT8(String timeStr, String format) {
        //https://cwind.iteye.com/blog/1908589
        TimeZone srcTimeZone = TimeZone.getTimeZone("GMT");
        TimeZone tarTimeZone = TimeZone.getTimeZone("GMT+8");
        Date parse = parse(timeStr, format);
        long tarTime = parse.getTime() - srcTimeZone.getRawOffset() + tarTimeZone.getRawOffset();
        return new Date(tarTime);
    }

    public static Date min(Date d1, Date d2) {
        return d1.before(d2) ? d1 : d2;
    }

    /**
     * @param d1 nutNull
     * @param d2 nutNull
     * @return d1 >= d2
     */
    public static boolean geq(Date d1, Date d2) {
        return !d1.before(d2);
    }

    /**
     * @return 1-周一;7-周日
     */
    public static int getWeekDay(Date date) {
        int res = use(date).c.get(Calendar.DAY_OF_WEEK);//2-周一;1-周日
        return res == 1 ? 7 : res - 1;
    }

    /**
     * @return ["一", "二",...]
     */
    public static String getWeekDayStr(Date date) {
        return WEEKDAY_CHN[getWeekDay(date) - 1];
    }

}
